闲话 22.10.28

闲话

明日CSP,而我连廊桥都不会分配
我是不是要抱泠了

于是今日晚上写一些板子

480p
soytony:斯大林p
这是什么新p主能不能各位给我科普一下

今日歌词是摘了美影日记里新的那部分!
今天推的歌是美影日记!希望各位能去看看新的pv

雨のち曇り晴れ

優しくきっと晴れ

きらきら浮かぶ言葉

もう忘れちゃうけれど

杂题

给定两个整数 n,m 和两个字符串 w1,w2n 是两个字符串的长度,m 是答案 P 的下界,n,m105

定义 Hash 函数如下

Hp(w)=i=0|w|1wirimodP

其中 P 是一个质数。

请你寻找任意一对 P,r 满足 Hp(w1)=Hp(w2),题目保证至少存在一组可行解。

mP1092rP2

仍然是翻译的官方题解。

问题可以被简单陈述如下:给定一个整系数多项式 f(x)=i=0n1fixi,找到一个大质数 p 以及 r[2,p2] 使得 f(r)0(modp)

一种解法是随机找到一个质数 p,并依次检查所有 r 的取值。由于对于 r 的随机值来说 f(r) mod p 也可以被认为是随机的,因此我们可以粗略地认为所有 r 都不满足要求的概率为

(11p)pe113

因此我们只需重复 O(1) 次质数的选取。使用多项式多点求值能做到大常数的 O(nlog2n)。这足以解决问题,但这不是最优秀的做法,也不是最优雅的方法 : )

下面将讲述另一种做法。我们令 dp1 的任意因子。众所周知,在模 p 整数域内有恰好 d 个元素 r 满足

rd1(modp)

注意到如果 r 是这样一个元素,则以 d=3 为例,有

r10+r5r3×3+1+r3+2(r3)3×r+r3×r2r+r2(modp)

事实上,如果我们将这一性质应用到整个多项式上,我们就可以将同余项的系数合并,得到一个更小度数的多项式

f(d)(r)=i=0d1αiri

这样求 d 个点只需要 O(d2) 的复杂度。

当前算法的过程如下:寻找一个小整数 d,选择一个形如 kd+1 的质数 p>m,随后找到所有满足 rd1(modp) 的整数 r,随后对这些位置求解 f(d)(r) 的值,祈祷着得到的值是 0
决定性的 trick 就是既然 f(d) 的系数能被一劳永逸地算出来,我们就可以对大量质数分别在 O(d2) 的复杂度内计算所有的 r 位置对应的值。
注意到,如果我们还相信 f(d)(modp) 的值是随机的(而且我们必须要相信),那大致上 md105d 个质数就应当足够了。

还剩下两个问题:1. 我们该选什么样的数 d? 2. 我们该如何找到满足 rd1(modp) 的整数 r
对于 1. ,我们发现最好的选择就是那些小质数,例如 3,5,7,11,。这样我们就能留下大量形如 kd+1 的质数 p,而且 O(d2) 的时间复杂度开销也不会很大。
对于 2. ,当选好了 d 后找到满足如上条件的整数 r 也不是很难。我们随机一个整数 r0,取 r0p1d 为对应整数。正确性考虑费马小定理。这一步实际选出并在之后使用的数是 r0p1d,因此其不可等于 1

具体地说,以下流程的算法跑得很好:

  1. 选择一个质数 d 以及一个质数 p=kd+1
  2. r{1,2,,p1} 中随机抽取一个值。
  3. rp1d1 进入下一步,反之回到上一步。
  4. 计算模 p 意义下的 f(d)(r),f(d)(r2),f(d)(rd)

可以证明,上述算法一定能够输出我们想要的答案。进一步的,由于每个 d 会产生 O(md) 个质数 p,每个质数 p 能够验证 O(d2) 个数字,因此取得 d 但无法得到所需答案的概率为

(11p)d2×pd=(11p)pd

可以看到期望复杂度很优秀。

code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 1e6 + 10;
int n, m, str[N];
char s[N], t[N]; 
 
int prime[N], cnt; bool vis[N];
void sieve(int bnd) {
    rep(i,2,bnd) {
        if (!vis[i]) prime[++cnt] = i;
        rep(j,1,cnt) {
            if (i * prime[j] > bnd) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        } 
    }
}
 
int qp(int a, int b, int mod) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
        b >>= 1;
    } return ret;
}
 
int a[N];
void check(int d) {
    rep(i,0,d-1) a[i] = 0;
    rep(i,0,n) a[i % d] += str[i];
    for (int p = m / d * d + d + 1; p < 1e6; p += d) if (!vis[p]) {
        int r, r0;
        while (true) {
            r = rand() * rand() % (p - 2) + 1;
            r0 = qp(r, (p-1) / d, p); 
            if (r0 == 1) continue;
            // cout << r << ' ' << r0 << endl;
            r = r0;
            rep(tmp,1,d) {
                int t = 0, bse = 1;
                rep(i,0,d-1) t = (t + 1ll * bse * a[i]) % p, bse = 1ll * bse * r % p;
                if (t == 0) { cout << p << ' ' << r << endl; exit(0); }
                r = 1ll * r0 * r % p;
            }
            break;
        }
    }
}
 
signed main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> n >> m >> s+1 >> t+1;
    rep(i,1,n) str[i] = s[i] - t[i];
    sieve(1e6);
    rep(i,2,cnt) check(prime[i]);
}



CF1276D

给定一棵n个点的树,点编号1n,第i条边连接aibi。初始时你有一个空的序列,树上的n个点都有标记。现在按照边的编号从小到大考虑每一条边:

  • 如果这一条边连接的两个点都有标记,则选择其中的一个点,擦除它的标记并将它的编号放入序列的末端;

  • 否则什么都不做。

求能够由上述操作得到的不同的序列数量mod998244353

n2×105

dp 题。

由于加边是有顺序的,考虑按照时间设计 dp 数组状态。

我们称一条边覆盖了一个点,当且仅当这条边擦除了这个点的标记。
然后设 fi,ji 节点子树的状态数,j 规定了 i 节点状态被擦除的情况。

  1. j=0 表示该节点被连向父亲的边前的连向儿子的边覆盖。
  2. j=1 表示该节点被连向父亲的边覆盖。
  3. j=2 表示该节点被连向父亲的边后的连向儿子的边覆盖。
  4. j=3 表示该节点没有被覆盖。

然后转移。

首先讨论被连向儿子的边覆盖。
枚举覆盖边连向的儿子。这个儿子 y 一定是在被连向父亲的边后被覆盖。转移值有 fy,2/3。然后枚举其他儿子。对应边编号在 y 之前的点 z 一定要被覆盖,转移值有 fz,0/1,在 y 之后的点 z 不一定被覆盖,转移值有 $f_{z,0/2/3}。
先不考虑 j 的取值对 y 的限制,列出转移式:

fx,0/2=y((fy,2+fy,3)×z<y(fz,0+fz,1)×zy(fz,0+fz,2+fz,3))

限制自行加入。

然后讨论被父亲覆盖。
由于当前节点是被父亲覆盖的,因此其孩子中所有小于父亲的边都不能在覆盖当前节点后再覆盖。由于这条边不会删除当前节点,因此当前边只能擦除子节点。因此转移 fy,0/1
相似的讨论说明对于大于父亲的节点不能选择 f1,因此转移 fy,0/2/3

fx,1=z<fa(fz,0+fz,1)×zfa(fz,0+fz,2+fz,3)

然后讨论未被覆盖。

fx,3=y(fz,0+fz,1)

转移即可。

code
#include <bits/stdc++.h>
using namespace std;
#ifdef ONLINE_JUDGE
    char buf[1<<21], *p1 = buf, *p2 = buf;  inline char getc() { return (p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++); }
    #define getchar getc
#endif
template <typename T> inline void get(T & u){
	u = 0; char ch = getchar(); bool f = 0; while (ch < '0' or ch > '9') f = f or ch == '-',  ch = getchar();
	while (ch >= '0' and ch <= '9') u = (u<<1) + (u<<3) + (ch^48), ch = getchar(); f and (u = -u);
} template <typename T, typename ... Args> inline void get(T & u, Args & ... _Args) { get(u); get(_Args...); }
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 5e5 + 10, mod = 998244353;
int n, t1, t2, f[N][4]; 
vector <int> g[N];

void dfs(int u, int fa) {
    for (auto v : g[u]) if (v != fa) dfs(v, u);
    static int cnt, fap, _01[N], _23[N], _023[N];
    cnt = fap = 0; _01[0] = 1;
    for (auto v : g[u]) if (v == fa) fap = cnt;
    else {
        ++cnt;
        _01[cnt] = 1ll * _01[cnt - 1] * (f[v][0] + f[v][1]) % mod;
        _23[cnt] = f[v][2] + f[v][3]; if (_23[cnt] >= mod) _23[cnt] -= mod;
        _023[cnt] = _23[cnt] + f[v][0]; if (_023[cnt] >= mod) _023[cnt] -= mod;
    } 
    _023[cnt + 1] = 1;
    pre(i,cnt - 1,1) _023[i] = 1ll * _023[i + 1] * _023[i] % mod;
    rep(i,1,cnt) 
        if (i <= fap) f[u][0] = (f[u][0] + 1ll * _01[i - 1] * _23[i] % mod * _023[i + 1]) % mod;
        else f[u][2] = (f[u][2] + 1ll * _01[i - 1] * _23[i] % mod * _023[i + 1]) % mod;
    f[u][1] = 1ll * _01[fap] * _023[fap + 1] % mod;
    f[u][3] = _01[cnt];
}

signed main() {
    get(n); rep(i,2,n) get(t1, t2), g[t1].emplace_back(t2), g[t2].emplace_back(t1);
    dfs(1, 0);
    cout << (1ll * f[1][0] + f[1][2] + f[1][3]) % mod;
}
posted @   joke3579  阅读(64)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示